Skip to content

build: add an ANYPS5_ENABLE_TSAN option for ThreadSanitizer builds - #11179

Merged
oneandonlydean merged 10 commits into
boykopovar:mainfrom
MarcZX10:marc/build-tsan-option
Oct 26, 2026
Merged

oneandonlydean merged 10 commits into
boykopovar:mainfrom
MarcZX10:marc/build-tsan-option

Conversation

@MarcZX10

Copy link
Copy Markdown
Contributor

What

The tree can be built with AddressSanitizer (-DANYPS5_ENABLE_ASAN=ON), but not with ThreadSanitizer, although the runtime is heavily threaded: guest pthreads, AGC queue workers, audio and equeue waiters. This adds -DANYPS5_ENABLE_TSAN=ON for Linux GCC and Clang builds.

  • Option: ANYPS5_ENABLE_TSAN compiles and links with -fsanitize=thread -fno-omit-frame-pointer and defines ANYPS5_ENABLE_TSAN=1. It is rejected with a configure error on other systems or compilers, and when combined with ANYPS5_ENABLE_ASAN. Like the ASan build, it keeps executables dynamically linked, because the sanitizer runtime cannot be linked statically.
  • Tests: guest_mlock and guest_memory skip their host mlock result checks under ThreadSanitizer, as they already do under ASan. The sanitizer's own mappings change what /proc/self/status reports and what a locked range can hold.
  • Suppressions: tools/tsan-suppressions.txt silences reports from Mesa's lavapipe, the LLVM it compiles shaders with, and PulseAudio. Those system libraries are not instrumented, so ThreadSanitizer cannot see their locking. Without the file, 94 AGC tests fail inside libvulkan_lvp.so. With lavapipe alone suppressed, 705 fail on lock-order reports inside libLLVM.
  • Docs: BUILD.md lists the flag and gets a short ThreadSanitizer section. It explains the suppressions, and that guest memory written by the emulated GPU while a guest thread polls it (labels, fences) is shared without C++ atomics on purpose, so those accesses are reported too.

Results. A full RelWithDebInfo build of this branch on an ubuntu-latest fork runner (GCC 15.2), with TSAN_OPTIONS=halt_on_error=1:second_deadlock_stack=1:suppressions=tools/tsan-suppressions.txt and ctest --timeout 600 --parallel 4: 2,274 tests, 2,112 passed, 88 skipped and 74 failed. The failures, by cause:

Cause Tests Status
Lock-order inversion between the app0.zar mutex and the directory mutex in libc 17 #10100 removes it (checked with this option: 3 affected tests fail before and pass after)
close racing an equeue file measurement 1 (guest_socket_events) #11125
scePthreadAttrGet reading _detached 1 (guest_pthread_stack) #10810 makes it atomic
Guest-memory polling by the emulated GPU (Pm4.cpp:581, GuestMemory.cpp label writes, and the tests that drive them) about 20 by design
Synchronization ThreadSanitizer cannot see: concurrent syscalls on one socket (recv), glibc flockfile around putc_unlocked, FFmpeg frame threading 5 false positives
No ThreadSanitizer report: call-trace abort tests timing out at 20 s, fibers and setcontext, RLIMIT_DATA, SEGVs in agc_driver_graphics* about 25 not threading
Others (Dispatch.cpp static profiling counters, libSceJobManager WorkerEntry, a few test-side plain flags) about 5 not triaged in this pull request

The option builds nothing differently unless it is turned on, and no workflow runs it yet.

Tested

  • The fork run above, plus two earlier runs that produced the suppression list (first without suppressions: 164 failures, 94 of them in lavapipe; then without the LLVM entries: 785 failures, 705 in libLLVM).
  • Configure-time checks: -DANYPS5_ENABLE_TSAN=ON -DANYPS5_ENABLE_ASAN=ON stops with ANYPS5_ENABLE_ASAN and ANYPS5_ENABLE_TSAN cannot be combined, and -DANYPS5_RELINKER_ONLY=ON -DANYPS5_ENABLE_TSAN=ON configures.
  • python3 tools/check_conventions.py --base upstream/main and git diff --check upstream/main pass. tools/pr_overlap.py --branch lists only pull requests that share CMakeLists.txt, docs/dev/BUILD.md or core/libs/tests/GuestMemory.cpp, with no conflicting lines. Windows and macOS are rejected by design and were not run.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source (not applicable; no third-party code added)
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

Declare a ThreadSanitizer switch next to ANYPS5_ENABLE_ASAN and reject it on other systems and compilers, as ASan does. The two sanitizers cannot share one process, so configuring both is a fatal error.
GCC rejects -fsanitize=thread together with -static ("cannot specify -static with -fsanitize=thread"), and the ThreadSanitizer runtime has to come up before libc. Leave the -static -static-libgcc -static-libstdc++ executable flags out when ANYPS5_ENABLE_TSAN is on, as the ASan branch already does.
Add the ThreadSanitizer compile and link options, keep frame pointers for usable report stacks, and define ANYPS5_ENABLE_TSAN=1 so sources and tests can tell a ThreadSanitizer build apart. The guest arena keeps its default range: it starts below ThreadSanitizer's application memory limit, and the arena already records host mappings such as the runtime's protected gaps as used ranges.
…hreadSanitizer

The sanitizer runtimes intercept mlock, munlock, mlockall and munlockall and return 0 without calling the kernel (TSAN_OPTIONS=verbosity=1 prints "ThreadSanitizer ignores mlock/mlockall/munlock/munlockall"). VmLck therefore never grows and mlock of an unmapped or reserved range succeeds, so the ENOMEM and locked-size checks that are already skipped under ANYPS5_ENABLE_ASAN are skipped under ANYPS5_ENABLE_TSAN too.
…ThreadSanitizer

CheckMlock reads VmLck and expects ENOMEM from mlock of reserved and unmapped ranges. ThreadSanitizer, like AddressSanitizer, replaces mlock and munlock with interceptors that return 0 without locking anything, so skip the same checks that ANYPS5_ENABLE_ASAN already skips. The guest arena range check stays as is: a ThreadSanitizer build keeps the default range.
@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 11042 core/libs/tests/GuestMlock.cpp L27-38
PR 11366 CMakeLists.txt L78-79

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 674 CMakeLists.txt
docs/dev/BUILD.md
PR 1835 CMakeLists.txt
docs/dev/BUILD.md
PR 3786 CMakeLists.txt
PR 4449 core/libs/tests/GuestMemory.cpp
PR 5021 CMakeLists.txt
core/libs/tests/GuestMemory.cpp
docs/dev/BUILD.md
PR 6321 core/libs/tests/GuestMemory.cpp
PR 7105 core/libs/tests/GuestMemory.cpp
PR 7201 CMakeLists.txt
PR 9508 CMakeLists.txt

@THEMANnt THEMANnt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 52e0132a34e117a197331c66a38a1382b5d3568a. Pending review.

Configuration branches are checked; Linux ThreadSanitizer runtime validation remains pending.

Validation: Executed the submitted CMake guards and flag branches: Linux GNU/Clang select thread sanitizer and dynamic linking; Windows, Darwin, unsupported compiler and ASan+TSan combinations refuse as intended. Source review also covers the sanitizer-specific mlock exclusions and suppressions.

Scope / next step: These are actual CMake branch checks with recorded commands, not native Linux TSan compilation or execution. Please retain that distinction until a supported Linux runtime run is available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants